/**
 * 
 */
package de.farbstreich.tozen2.server.games;

import java.util.HashMap;
import java.util.Map.Entry;

import de.farbstreich.tozen2.common.TozenMessageType;
import de.farbstreich.tozen2.common.TozenProtocolMessage;
import de.farbstreich.tozen2.server.GameAlreadyStartedException;
import de.farbstreich.tozen2.server.PlayerThread;
import de.farbstreich.tozen2.server.TozenServer;
import de.farbstreich.tozen2.server.games.Game.Tile;
import de.farbstreich.tozen2.server.games.Game.TileEnum;

/**
 * @author Martin Spielmann (2008055519)
 * The one and only game.
 * This class holds all rules.
 */
public class TozenOriginal extends Game {

	private enum Place {
		TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT;
	}

	private HashMap<PlayerThread, Place> playerPlace;

	/**
	 * @param name
	 * @param maxPlayersCount
	 * @param minPlayersCount
	 */
	public TozenOriginal(String name, PlayerThread admin, TozenServer server,
			GameTypeAbstract type) {
		super(name, admin, server, type);
		playerPlace = new HashMap<PlayerThread, Place>();
		this.board = new Tile[12][12];

	}



	@Override
	public boolean tryMove(Integer startX, Integer startY, Integer targetX,
			Integer targetY) {
		System.out.println("TryMove:" + startX + "," + startY + ";" + targetX
				+ "," + targetY);
		boolean valid = false;
		Tile start = null;
		Tile target = null;
		try {
			start = this.board[startX][startY];
			target = this.board[targetX][targetY];
		} catch (ArrayIndexOutOfBoundsException e1) {
			this.currentPlayer
					.sendInfoMessage("Vergiss die Sache mit dem: 'Ich bin cool, weil ich neben dem Spielfeld spiele'!");
		}

		// Ist da auch eine Figur?
		if (start == null) {
			this.currentPlayer
					.sendInfoMessage("Öhm..nein. Luft kannst du in diesem Spiel nicht bewegen. Bitte wähle doch eine Figur aus richtig Fleisch und Blut.");
		} else // Wenn ja, ist es auch eine des aktuellen Spielers?
		if (start.player != this.currentPlayer) {
			this.currentPlayer
					.sendInfoMessage("Bedenke stehts: Dieses Programm wurde (zumindest teilweise) von hoch intelligenten Menschen geschrieben. Es ist gerade deswegen NICHT möglich die Figur eines Gegners zu bewegen.");
		} else // Ist das Ziel noch auf dem Spielfeld
		if (this.board.length <= targetX
				|| this.board[targetX].length <= targetY) {
			this.currentPlayer
					.sendInfoMessage("Du kannst deine Figur nicht aus dem Spielfeld ziehen. Ein weiterer Versuch ist übrigens zwecklos!");
		} else // Stimmt die Richtung (Diagonal oder vertikal)
		if (startX != targetX && startY != targetY) {
			this.currentPlayer
					.sendInfoMessage("Du kannst mit deinen Figuren beliebig weit in horizontaler oder vertikaler Richtung ziehen. Das ist aber auch schon alles. Ach ja: Du kannst andere Figuren auch überspringen.");
		} else // Wenn das Zielfeld frei ist, ist alles paletti.. es kann
		// gezogen werden
		if (target == null) {
			valid = true;
		} else // Wenn die Figur auf dem Zielfeld eine eigene ist..
		if (target.player == this.currentPlayer) {
			this.currentPlayer
					.sendInfoMessage("Deine Gegner scheinen sehr schlecht zu spielen. Oder wieso willst du deine eigenen Figuren schlagen?");
		} else { // Wenn die Figur auf dem Zielfeld einem anderen gehört:
			int distance = Math.abs((targetX - startX) + (targetY - startY));

			if (start.tile == TileEnum.ARCHER) {
				if (distance == 4) {
					this.currentPlayer
							.sendInfoMessage("Harharhar. Du hast deinem Gegner soeben einen Figur genommen.");
					valid = true;
				} else {
					this.currentPlayer
							.sendInfoMessage("Pfeilbogenschützen können nur über vier Felder hinweg gegnerische Figuren schlagen.");
				}
			} else if (start.tile == TileEnum.KNIGHT) {
				if (distance == 3) {
					this.currentPlayer
							.sendInfoMessage("Ein spannender Kampf..und du hast gewonnen. Dein Gegner ist eine Figur ärmer.");
					valid = true;
				} else {
					this.currentPlayer
							.sendInfoMessage("Ein Ritter schlägt Gegner nur über 3 Felder.");
				}
			} else if (start.tile == TileEnum.TOWER) {
				if (distance == 5 || distance == 2) {
					this.currentPlayer
							.sendInfoMessage("Harharhar. Dein Turm hat zugeschlagen.");
					valid = true;
				} else {
					this.currentPlayer
							.sendInfoMessage("Der Turm kann viel, aber nicht alles. Gegner kann er über 2 und 5 Felder hinweg schlagen.");
				}
			}
		}
		if (valid == true)
			this.move(startX, startY, targetX, targetY);

		// Schlagen eines Turms...alle Figuren gehen an den "Schläger"
		if (valid == true && target != null && target.tile == TileEnum.TOWER) {
			PlayerThread targetPlayer = target.player;

			for (int x = 0; x < this.board.length; x++) {
				for (int y = 0; y < this.board[x].length; y++) {
					if (this.board[x][y] != null
							&& this.board[x][y].player == targetPlayer)
						this.board[x][y].player = this.currentPlayer;
				}
			}
			this.sendsInfoMessage("Harharhar. Die Figuren von "
					+ targetPlayer.getPlayerName() + " gingen soeben alle an "
					+ this.currentPlayer.getPlayerName()
					+ " über. Denn er hat den seinen Turm geschlagen!");
			this.removePlayer(targetPlayer);
		}

		// Eine Gewinnsituation?

		// Gewinn duch eroberung von 3 bzw. 4 Throne
		// TODO: Dieses Zeug optimieren
		HashMap<PlayerThread, Integer> tilesOnThrone = new HashMap<PlayerThread, Integer>();
		for (PlayerThread p : this.players.values())
			tilesOnThrone.put(p, new Integer(0));

		if (this.board[4][4] != null)
			tilesOnThrone.put(this.board[4][4].player, tilesOnThrone.get(
					this.board[4][4].player).intValue() + 1);
		if (this.board[7][4] != null)
			tilesOnThrone.put(this.board[7][4].player, tilesOnThrone.get(
					this.board[7][4].player).intValue() + 1);
		if (this.board[7][7] != null)
			tilesOnThrone.put(this.board[7][7].player, tilesOnThrone.get(
					this.board[7][7].player).intValue() + 1);
		if (this.board[4][7] != null)
			tilesOnThrone.put(this.board[4][7].player, tilesOnThrone.get(
					this.board[4][7].player).intValue() + 1);

		int max = 0;
		PlayerThread maxPlayer = null;
		for (Entry<PlayerThread, Integer> e : tilesOnThrone.entrySet())
			if (e.getValue() > max) {
				max = e.getValue();
				maxPlayer = e.getKey();
			}
		if (maxPlayer != null) {
			if (max >= 3 && (this.players.size() >= 3)) {
				this.winner(maxPlayer);
				return true;
				
			} else if (max == 4 && this.players.size() == 2) {
				this.winner(maxPlayer);
				return true;
				
			}
		}

		if (valid == true)
			this.nextPlayer();
		else
			this.sendCurrentMessage();

		return valid;
	}

	@Override
	void winner(PlayerThread p) {
		this.sendsInfoMessage("Herzliche Gratulation! " + p.getPlayerName()
				+ " hat dieses Spiel gewonnen!");
		TozenProtocolMessage msg = new TozenProtocolMessage(
				TozenMessageType.END_GAME);
		this.sendProtocolMessage(msg);
		this.killGame();
		this.server.removeGame(this);

	}
	


	public void sendsInfoMessage(String msg) {
		for (PlayerThread p : this.players.values())
			p.sendInfoMessage(msg);
	}

	public void sendProtocolMessage(TozenProtocolMessage msg) {
		for (PlayerThread p : this.players.values())
			p.sendProtocolMessage(msg);
	}

	public void startGame() throws NotRightPlayerCountException,
			GameAlreadyStartedException {
		super.startGame();

		this.board = new Tile[12][12];

		int k = 0;
		// Startpositionen setzen
		for (PlayerThread p : this.players.values()) {
			switch (k) { // Oben links
			case 0:
				this.board[0][0] = new Tile(p, TileEnum.TOWER);
				this.board[0][1] = new Tile(p, TileEnum.ARCHER);
				this.board[0][2] = new Tile(p, TileEnum.ARCHER);
				this.board[0][3] = new Tile(p, TileEnum.ARCHER);
				this.board[1][0] = new Tile(p, TileEnum.ARCHER);
				this.board[2][0] = new Tile(p, TileEnum.ARCHER);
				this.board[3][0] = new Tile(p, TileEnum.ARCHER);
				this.board[1][1] = new Tile(p, TileEnum.KNIGHT);
				this.board[1][2] = new Tile(p, TileEnum.KNIGHT);
				this.board[2][1] = new Tile(p, TileEnum.KNIGHT);
				this.board[2][2] = new Tile(p, TileEnum.KNIGHT);
				playerPlace.put(p, Place.TOP_LEFT);
				break;
			case 1: // Unten rechts
				this.board[11][11] = new Tile(p, TileEnum.TOWER);
				this.board[11][10] = new Tile(p, TileEnum.ARCHER);
				this.board[11][9] = new Tile(p, TileEnum.ARCHER);
				this.board[11][8] = new Tile(p, TileEnum.ARCHER);
				this.board[10][11] = new Tile(p, TileEnum.ARCHER);
				this.board[9][11] = new Tile(p, TileEnum.ARCHER);
				this.board[8][11] = new Tile(p, TileEnum.ARCHER);
				this.board[10][10] = new Tile(p, TileEnum.KNIGHT);
				this.board[10][9] = new Tile(p, TileEnum.KNIGHT);
				this.board[9][10] = new Tile(p, TileEnum.KNIGHT);
				this.board[9][9] = new Tile(p, TileEnum.KNIGHT);
				playerPlace.put(p, Place.BOTTOM_RIGHT);
				break;
			case 2: // Oben rechts
				this.board[11][0] = new Tile(p, TileEnum.TOWER);
				this.board[11][1] = new Tile(p, TileEnum.ARCHER);
				this.board[11][2] = new Tile(p, TileEnum.ARCHER);
				this.board[11][3] = new Tile(p, TileEnum.ARCHER);
				this.board[10][0] = new Tile(p, TileEnum.ARCHER);
				this.board[9][0] = new Tile(p, TileEnum.ARCHER);
				this.board[8][0] = new Tile(p, TileEnum.ARCHER);
				this.board[9][1] = new Tile(p, TileEnum.KNIGHT);
				this.board[9][2] = new Tile(p, TileEnum.KNIGHT);
				this.board[10][1] = new Tile(p, TileEnum.KNIGHT);
				this.board[10][2] = new Tile(p, TileEnum.KNIGHT);
				playerPlace.put(p, Place.TOP_RIGHT);
				break;
			case 3: // Unten links
				this.board[0][11] = new Tile(p, TileEnum.TOWER);
				this.board[0][10] = new Tile(p, TileEnum.ARCHER);
				this.board[0][9] = new Tile(p, TileEnum.ARCHER);
				this.board[0][8] = new Tile(p, TileEnum.ARCHER);
				this.board[1][11] = new Tile(p, TileEnum.ARCHER);
				this.board[2][11] = new Tile(p, TileEnum.ARCHER);
				this.board[3][11] = new Tile(p, TileEnum.ARCHER);
				this.board[1][10] = new Tile(p, TileEnum.KNIGHT);
				this.board[1][9] = new Tile(p, TileEnum.KNIGHT);
				this.board[2][10] = new Tile(p, TileEnum.KNIGHT);
				this.board[2][9] = new Tile(p, TileEnum.KNIGHT);
				playerPlace.put(p, Place.BOTTOM_LEFT);
				break;
			}

			k++;
		}

		this.sendGameInfos();
		this.sendPositions();

		// Erster Spieler festsetzen
		this.nextPlayer();
	}

	@Override
	void nextPlayer() {

		PlayerThread op = this.currentPlayer;
		this.currentPlayerPosition++;
		if(this.currentPlayerPosition>=this.order.size())
			this.currentPlayerPosition = 0;
		
		this.currentPlayer = this.order.get(this.currentPlayerPosition);


		this.sendCurrentMessage();

		// Hat dieser Spieler gewonnen, weil er mit dem Turm über einen Zug auf
		// dem gegeüberliegenden Thron stand?
		if ((playerPlace.get(this.currentPlayer) == Place.TOP_LEFT
				&& this.board[7][7] != null
				&& this.board[7][7].tile == TileEnum.TOWER && this.board[7][7].player == this.currentPlayer)
				|| (playerPlace.get(this.currentPlayer) == Place.TOP_RIGHT
						&& this.board[4][7] != null
						&& this.board[4][7].tile == TileEnum.TOWER && this.board[4][7].tile == TileEnum.TOWER)
				|| (playerPlace.get(this.currentPlayer) == Place.BOTTOM_RIGHT
						&& this.board[4][4] != null
						&& this.board[4][4].tile == TileEnum.TOWER && this.board[4][4].tile == TileEnum.TOWER)
				|| (playerPlace.get(this.currentPlayer) == Place.BOTTOM_LEFT
						&& this.board[7][4] != null
						&& this.board[7][4].tile == TileEnum.TOWER && this.board[7][4].tile == TileEnum.TOWER)) {
			this.winner(this.currentPlayer);
		}

	}

}
